package com.eryos.android.cigarettecounter.charts;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.widget.Toast;

import com.eryos.android.cigarettecounter.R;
import com.eryos.android.cigarettecounter.io.DataExporter;

/**
 * Project : CigaretteCounter Author : Olivier Combe URL : http://eryos.fr/CigaretteCounter/
 * 
 * Copyright(C)2011 - Olivier Combe
 * 
 * Licensed under the Apache License, Version 2.0(the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 * 
 * @version \$Revision.*\$
 * @author Olivier Combe
 */
public abstract class Chart {

    /** *************** Static Variables. *************** **/
    private static final String LOG_TAG = "Chart";

    /* Private & Static Default Values & Constants */

    /** Width / Height ratio. */
    private static double CHART_RATIO = 0.625;
    private static boolean SHOW_MSG = false;
    private static boolean DATA_AVAILABLE = true;

    /** Root URL for Google Chart. */
    public static final String ROOT_URL = "http://chart.apis.google.com/chart?";

    /* Public & Static Default Values & Constants */
    /** Default/Blank value. */
    public static final String BLANK_VALUE = "0";

    /** Default Chart Width. */
    protected static int DEFAULT_CHART_WIDTH = 320;
    /** Default Chart Height. */
    public static int DEFAULT_CHART_HEIGHT = 200;
    /** Default Thumb Width. */
    public static int DEFAULT_THUMB_WIDTH = 320;
    /** Default Thumb Height. */
    public static int DEFAULT_THUMB_HEIGHT = 200;

    /** Data Separator. */
    public static final String DATA_SEPARATOR = ",";
    /** Field Separator. */
    public static final String LABEL_SEPARATOR = "|";

    /* Chart arguments / parameters */
    protected String type = "lc";

    /** Chart width. */
    protected int width = -1;
    /** Chart Height. */
    protected int height = -1;
    /** Thumbnail width. */
    protected int thumbWidth = -1;
    /** Thumbnail height. */
    protected int thumbHeight = -1;
    protected String bgColor = "ffffff";
    protected String title = null;
    protected String titleColor = "000000";
    protected double titleSize = 12.5;
    protected double textSize = 10.5;
    protected String axisColor = "000000";

    protected String data = "";

    protected int maxYValue = 22;
    protected int minYValue = 0;
    protected int maxXValue = 31;
    protected int minXValue = 0;

    /** Chart URL. */
    protected String url;
    /** Thumbnail URL. */
    protected String thumbURL;

    protected Bitmap chartBitmap;
    protected Bitmap thumbBitmap;

    /**
     * Default Chart Constructor.
     */
    public Chart() {
    }

    /**
     * Used to generate the Google Chart API based URL for main chart.
     * 
     * @return : url based on chart size
     */
    public String getURL() {
        if (width < 0) {
            url = generateURL(DEFAULT_CHART_WIDTH, DEFAULT_CHART_HEIGHT);
        } else {
            url = generateURL(width, height);
        }

        return url;
    }

    /**
     * Used to generate the Google Chart API based URL for thumb chart.
     * 
     * @return : url depending on chart thumb size
     */
    public String getThumbURL() {
        if (thumbWidth < 0) {
            thumbURL = generateURL(DEFAULT_THUMB_WIDTH, DEFAULT_THUMB_HEIGHT);
        } else {
            thumbURL = generateURL(thumbWidth, thumbHeight);
        }

        return thumbURL;
    }

    /**
     * Used to generate the Google Chart API based url for specific chart size.
     * 
     * @param widthArg
     *            : chart width
     * @param heightArg
     *            : chart height
     * @return : url for specific size
     */
    protected abstract String generateURL(int widthArg, int heightArg);

    /******************* Setters *******************/

    /******************* Getters *******************/
    /**
     * Return the data.
     * 
     * @return the Data
     */
    public String getData() {
        return data;
    }

    /**
     * Return the TitleSize.
     * 
     * @return the TitleSize
     */
    public double getTitleSize() {
        return titleSize;
    }

    /**
     * Add a new value to data set.
     * 
     * @param valueArg
     *            to add
     */
    public void addDataValue(final String valueArg) {
        if (data == null || data.length() == 0) {
            data = valueArg;
        } else {
            data = data + Chart.DATA_SEPARATOR + valueArg;
        }
    }

    /**
     * Add a new value to data set.
     * 
     * @param valueArg
     *            to add
     */
    public void addDataValue(final int valueArg) {
        addDataValue(String.valueOf(valueArg));
    }

    /**
     * Add a new value to data set.
     * 
     * @param valueArg
     *            to add
     */
    public void addDataValue(final double valueArg) {
        addDataValue(String.valueOf(valueArg));
    }

    /**
     * Define chart data, values must be separated with {@DATA_SEPARATOR}.
     * 
     * @param dataArg
     *            to set
     */
    public void setData(final String dataArg) {

        if (dataArg.length() > DATA_SEPARATOR.length() && dataArg.endsWith(DATA_SEPARATOR)) {
            this.data = dataArg.substring(0, dataArg.length() - Chart.DATA_SEPARATOR.length());
        } else {
            this.data = dataArg;
        }
    }

    /**
     * Define Title size.
     * 
     * @param titleSizeArg
     *            to set
     */
    public void setTitleSize(final double titleSizeArg) {
        textSize = titleSizeArg * 0.8;
        this.titleSize = titleSizeArg;
    }

    /* ********************************************************************** */
    /* STATIC METHODS */
    /* ********************************************************************** */

    /**
     * Set the DefaultChartWidth.
     * 
     * @param sizeArg
     *            to set
     */
    private static void setDefaultChartWidth(final int sizeArg) {
        DEFAULT_CHART_WIDTH = sizeArg;
    }

    /**
     * Set the DefaultChartHeight.
     * 
     * @param sizeArg
     *            to set
     */
    private static void setDefaultChartHeight(final int sizeArg) {
        DEFAULT_CHART_HEIGHT = sizeArg;
    }

    /**
     * Set the DefaultThumbWidth.
     * 
     * @param sizeArg
     *            to set
     */
    private static void setDefaultThumbWidth(final int sizeArg) {
        DEFAULT_THUMB_WIDTH = sizeArg;
    }

    /**
     * Set the DefaultThumbHeight.
     * 
     * @param sizeArg
     *            to set
     */
    public static void setDefaultThumbHeight(final int sizeArg) {
        DEFAULT_THUMB_HEIGHT = sizeArg;
    }

    /**
     * Define the size of the chart(overview and fullscreen)based on screen size.
     * 
     * @param screenWidthArg
     *            Device Screen Width
     * @param screenHeightArg
     *            Device Screen Heigh
     */
    public static void defineChartDefaultSize(final int screenWidthArg, final int screenHeightArg) {
        Log.d(LOG_TAG, "defineChartDefaultSize");

        Log.d(LOG_TAG, "SCREEN_WIDTH : " + screenWidthArg);
        Log.d(LOG_TAG, "SCREEN_HEIGHT : " + screenHeightArg);

        int overviewWidth = 320;
        int overviewHeight = 200;
        int chartWidth = 320;
        int chartHeight = 200;

        if (screenWidthArg > screenHeightArg) {
            // Landscape
            if (screenHeightArg < 400) {
                overviewWidth = (int) (0.67 * screenWidthArg);
                overviewHeight = (int) (CHART_RATIO * overviewWidth);
            }
        } else {
            // Portrait
            if (screenWidthArg < 400) {
                overviewWidth = (int) (0.67 * screenWidthArg);
                overviewHeight = (int) (CHART_RATIO * overviewWidth);
            }
        }

        chartWidth = (int) (0.95 * screenWidthArg);
        chartHeight = (int) (CHART_RATIO * chartWidth);

        /* Google Chart API Limit is 300 000 pixels per charts */
        while (chartWidth * chartHeight >= 300000) {
            chartWidth = (int) (chartWidth * 0.95);
            chartHeight = (int) (chartHeight * 0.95);
        }

        /* reduce to the size of the popup */
        chartWidth = (int) (0.95 * chartWidth);
        chartHeight = (int) (0.95 * chartHeight);

        Chart.setDefaultChartWidth(chartWidth);
        Chart.setDefaultChartHeight(chartHeight);
        Chart.setDefaultThumbWidth(overviewWidth);
        Chart.setDefaultThumbHeight(overviewHeight);

        /* Trace sizes */
        Log.d(LOG_TAG, "FINAL CHART_WIDTH : " + DEFAULT_CHART_WIDTH);
        Log.d(LOG_TAG, "FINAL CHART_HEIGHT : " + DEFAULT_CHART_HEIGHT);
        Log.d(LOG_TAG, "OVERVIEW_WIDTH : " + DEFAULT_THUMB_WIDTH);
        Log.d(LOG_TAG, "OVERVIEW_HEIGHT : " + DEFAULT_THUMB_HEIGHT);
    }

    /**
     * Download a chart from a URL an return a bitmap.
     * 
     * @param ctx
     *            : Application context
     * @param url
     *            : the url used to generate the chart
     * @return Bitmap : Bitmap of the chart
     * @throws IOException
     *             : gg
     * @throws UnknownHostException
     *             : gg
     */
    public static Bitmap downloadChartAsBitmap(final Context ctx, final String url) throws UnknownHostException,
            IOException {
        Log.i("CigaretteCounter", "downloadBitmap :" + url);

        if (DATA_AVAILABLE) {
            Bitmap bm = null;
            try {
                /* Open a connection and connect with URL */
                URL aURL = new URL(url);
                URLConnection conn = aURL.openConnection();
                conn.connect();

                /* Open stream to retrieve data */
                InputStream is = conn.getInputStream();
                BufferedInputStream bis = new BufferedInputStream(is);

                /* Retrieve data */
                bm = BitmapFactory.decodeStream(bis);

                /* Close stream */
                bis.close();
                is.close();

                /* Return result */
                Log.d("CigaretteCounter", "downloadBitmap : OK");
                return bm;

            } catch (UnknownHostException e) {
                /* Web Service not available */
                DATA_AVAILABLE = false;
                Log.w("CigaretteCounter", "downloadBitmap No available connection");

                if (!SHOW_MSG) {
                    Toast.makeText(ctx, "No available connection, please retry later ...", Toast.LENGTH_SHORT).show();
                    SHOW_MSG = true;
                }

                throw e;
            } catch (IOException e) {
                /* Other IO exception */
                DATA_AVAILABLE = false;
                Log.e("CigaretteCounter", "downloadBitmap Erreur IO", e);
                throw e;
            }
        }

        /* If code reach this point, we must return the default/blank/error chart */
        return getDefaultChartPicture(ctx);
    }

    /**
     * Return the default chart(used while loading).
     * 
     * @param ctx
     *            : Context of the application
     * @return : Bitmap : default chart
     */
    public static Bitmap getDefaultChartPicture(final Context ctx) {
        Drawable errorDrawable = ctx.getResources().getDrawable(R.drawable.eryos); // TODO change bitmap
        return ((BitmapDrawable) errorDrawable).getBitmap();
    }

    /**
     * Write a Bitmap to a specific file on SD Cart.
     * 
     * @param bmp
     *            : Bitmap to write
     * @param filename
     *            : destination file
     * @return : PathName of the file
     */
    public static String writeBitmapToSD(final Bitmap bmp, final String filename) {

        /* Convert Bitmap object in ByteArray to write it to filesystem */
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        bmp.compress(Bitmap.CompressFormat.PNG, 100, bytes);

        try {

            /* Create and open strem */
            DataExporter export = new DataExporter(filename + ".png");

            /* Write data */
            if (export.openStream()) {
                export.writeDataToExternalFile(bytes.toByteArray());
            }

            /* Close stream */
            export.closeStream();

            return export.getFullPathName();

        } catch (FileNotFoundException e) {
            Log.e(LOG_TAG, "", e);
        } catch (IOException e) {
            Log.e(LOG_TAG, "", e);
        }

        return "";
    }

    /**
     * @return the width
     */
    public int getWidth() {
        return width;
    }

    /**
     * @param widthArg
     *            the width to set
     */
    public void setWidth(final int widthArg) {
        this.width = widthArg;
    }

    /**
     * @return the height
     */
    public int getHeight() {
        return height;
    }

    /**
     * @param heightArg
     *            the height to set
     */
    public void setHeight(final int heightArg) {
        this.height = heightArg;
    }

    /**
     * @return the bgColor
     */
    public String getBgColor() {
        return bgColor;
    }

    /**
     * @param bgColorArg
     *            the bgColor to set
     */
    public void setBgColor(final String bgColorArg) {
        this.bgColor = bgColorArg;
    }

    /**
     * @return the title
     */
    public String getTitle() {
        return title;
    }

    /**
     * @param titleArg
     *            the title to set
     */
    public void setTitle(final String titleArg) {
        this.title = titleArg;
    }

    /**
     * @return the titleColor
     */
    public String getTitleColor() {
        return titleColor;
    }

    /**
     * @param titleColorArg
     *            the titleColor to set
     */
    public void setTitleColor(final String titleColorArg) {
        this.titleColor = titleColorArg;
    }

    /**
     * @return the maxYValue
     */
    public int getMaxYValue() {
        return maxYValue;
    }

    /**
     * @param maxYValueArg
     *            the maxYValue to set
     */
    public void setMaxYValue(final int maxYValueArg) {
        this.maxYValue = maxYValueArg;
    }

    /**
     * @return the minYValue
     */
    public int getMinYValue() {
        return minYValue;
    }

    /**
     * @param minYValueArg
     *            the minYValue to set
     */
    public void setMinYValue(final int minYValueArg) {
        this.minYValue = minYValueArg;
    }

    /**
     * @return the maxXValue
     */
    public int getMaxXValue() {
        return maxXValue;
    }

    /**
     * @param maxXValueArg
     *            the maxXValue to set
     */
    public void setMaxXValue(final int maxXValueArg) {
        this.maxXValue = maxXValueArg;
    }

    /**
     * @return the minXValue
     */
    public int getMinXValue() {
        return minXValue;
    }

    /**
     * @param minXValueArg
     *            the minXValue to set
     */
    public void setMinXValue(final int minXValueArg) {
        this.minXValue = minXValueArg;
    }

    /**
     * @return the thumbWidth
     */
    public int getThumbWidth() {
        return thumbWidth;
    }

    /**
     * @param thumbWidthArg
     *            the thumbWidth to set
     */
    public void setThumbWidth(final int thumbWidthArg) {
        this.thumbWidth = thumbWidthArg;
    }

    /**
     * @return the thumbHeight
     */
    public int getThumbHeight() {
        return thumbHeight;
    }

    /**
     * @param thumbHeightArg
     *            the thumbHeight to set
     */
    public void setThumbHeight(final int thumbHeightArg) {
        this.thumbHeight = thumbHeightArg;
    }

    /**
     * @return the chartBitmap
     */
    public Bitmap getChartBitmap() {
        return chartBitmap;
    }

    /**
     * @param chartBitmapArg
     *            the chartBitmap to set
     */
    public void setChartBitmap(final Bitmap chartBitmapArg) {
        this.chartBitmap = chartBitmapArg;
    }

    /**
     * @return the thumbBitmap
     */
    public Bitmap getThumbBitmap() {
        return thumbBitmap;
    }

    /**
     * @param thumbBitmapArg
     *            the thumbBitmap to set
     */
    public void setThumbBitmap(final Bitmap thumbBitmapArg) {
        this.thumbBitmap = thumbBitmapArg;
    }

    /**
     * @param thumbURLArg
     *            the thumbURL to set
     */
    public void setThumbURL(final String thumbURLArg) {
        this.thumbURL = thumbURLArg;
    }

}
